package com.lzl.web3browser;

import static com.lzl.web3browser.widget.AWalletAlertDialog.WARNING;

import static org.web3j.protocol.core.methods.request.Transaction.createFunctionCallTransaction;

import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;

import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.alphawallet.token.entity.EthereumMessage;
import com.alphawallet.token.entity.EthereumTypedMessage;
import com.alphawallet.token.entity.Signable;
import com.alphawallet.token.tools.Numeric;
import com.lzl.web3browser.entity.AnalyticsProperties;
import com.lzl.web3browser.entity.DAppFunction;
import com.lzl.web3browser.entity.NetworkInfo;
import com.lzl.web3browser.entity.SignAuthenticationCallback;
import com.lzl.web3browser.entity.URLLoadInterface;
import com.lzl.web3browser.entity.Wallet;
import com.lzl.web3browser.entity.WalletType;
import com.lzl.web3browser.entity.tokens.Token;
import com.lzl.web3browser.repository.EthereumNetworkBase;
import com.lzl.web3browser.repository.TokenRepository;
import com.lzl.web3browser.service.KeyService;
import com.lzl.web3browser.ui.widget.OnDappHomeNavClickListener;
import com.lzl.web3browser.ui.widget.entity.ActionSheetCallback;
import com.lzl.web3browser.ui.widget.entity.DappBrowserSwipeInterface;
import com.lzl.web3browser.ui.widget.entity.ItemClickListener;
import com.lzl.web3browser.viewmodel.DappBrowserViewModel;
import com.lzl.web3browser.web3.OnEthCallListener;
import com.lzl.web3browser.web3.OnSignMessageListener;
import com.lzl.web3browser.web3.OnSignPersonalMessageListener;
import com.lzl.web3browser.web3.OnSignTransactionListener;
import com.lzl.web3browser.web3.OnSignTypedMessageListener;
import com.lzl.web3browser.web3.OnWalletActionListener;
import com.lzl.web3browser.web3.OnWalletAddEthereumChainObjectListener;
import com.lzl.web3browser.web3.Web3View;
import com.lzl.web3browser.web3.entity.Address;
import com.lzl.web3browser.web3.entity.WalletAddEthereumChainObject;
import com.lzl.web3browser.web3.entity.Web3Call;
import com.lzl.web3browser.web3.entity.Web3Transaction;
import com.lzl.web3browser.widget.AWalletAlertDialog;
import com.lzl.web3browser.widget.ActionSheetDialog;
import com.lzl.web3browser.widget.TestNetDialog;

import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.http.HttpService;

import dagger.hilt.android.AndroidEntryPoint;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;

//@AndroidEntryPoint
public class MainActivity extends AppCompatActivity implements OnSignTransactionListener, OnSignPersonalMessageListener,
        OnSignTypedMessageListener, OnSignMessageListener, OnEthCallListener, OnWalletAddEthereumChainObjectListener,
        OnWalletActionListener, URLLoadInterface, ItemClickListener, OnDappHomeNavClickListener, DappBrowserSwipeInterface,
        SignAuthenticationCallback, ActionSheetCallback, TestNetDialog.TestNetDialogCallback , LifecycleObserver{


    private static final String WALLETCONNECT_CHAINID_ERROR = "Error: ChainId missing or not supported";


    private final Handler handler = new Handler(Looper.getMainLooper());


    private AWalletAlertDialog resultDialog;

    private AWalletAlertDialog chainSwapDialog;


    private ProgressBar progressBar;
    Web3View web3;
    Button button;


    private DAppFunction dAppFunction;
    private ActionSheetDialog confirmationDialog;

    private DappBrowserViewModel viewModel;
    private NetworkInfo activeNetwork;
    private Wallet wallet;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        wallet=new Wallet("0xf52bbB01904a9B4ddc75aAbb028b618Cc7CD151F");
        web3 = findViewById(R.id.web3view);
        button = findViewById(R.id.button);
        progressBar = findViewById(R.id.progressBar);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                web3.loadUrl("http://m.txswapfinder.xyz");
                web3.requestFocus();
            }
        });

        web3.setChainId(56);
        web3.setRpcUrl("https://rpc.ankr.com/eth");
        web3.setWalletAddress(new Address("0xf52bbB01904a9B4ddc75aAbb028b618Cc7CD151F"));
        web3.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView webview, int newProgress)
            {
                if (newProgress == 100)
                {
                    progressBar.setVisibility(View.GONE);
                }else {
                    progressBar.setVisibility(View.VISIBLE);
                    progressBar.setProgress(newProgress);
                }
            }

            @Override
            public boolean onConsoleMessage(ConsoleMessage msg)
            {
                boolean ret = super.onConsoleMessage(msg);

                if (msg.messageLevel() == ConsoleMessage.MessageLevel.ERROR)
                {
                    if (msg.message().contains(WALLETCONNECT_CHAINID_ERROR))
                    {
                        displayCloseWC();
                    }
                }

                return ret;
            }

            @Override
            public void onReceivedTitle(WebView view, String title)
            {
                super.onReceivedTitle(view, title);
//                currentWebpageTitle = title;
            }

            @Override
            public void onPermissionRequest(final PermissionRequest request)
            {
                Toast.makeText(MainActivity.this, "onPermissionRequest", Toast.LENGTH_LONG).show();
//                requestCameraPermission(request);
            }

            @Override
            public void onGeolocationPermissionsShowPrompt(String origin,
                                                           GeolocationPermissions.Callback callback)
            {
                super.onGeolocationPermissionsShowPrompt(origin, callback);
                Toast.makeText(MainActivity.this, "onGeolocationPermissionsShowPrompt", Toast.LENGTH_LONG).show();
//                requestGeoPermission(origin, callback);
            }

            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                             FileChooserParams fCParams)
            {
                if (filePathCallback == null) {
                    return true;
                }
//                uploadMessage = filePathCallback;
//                fileChooserParams = fCParams;
//                if (checkReadPermission()) {
//                    return requestUpload();
//                } else {
//                    return true;
//                }

                return true;
            }
        });
        web3.setWebViewClient(new WebViewClient() {


            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url)
            {
                String[] prefixCheck = url.split(":");
                if (prefixCheck.length > 1)
                {
                    Intent intent;
                    switch (prefixCheck[0])
                    {
                        case C.DAPP_PREFIX_TELEPHONE:
                            intent = new Intent(Intent.ACTION_DIAL);
                            intent.setData(Uri.parse(url));
                            startActivity(Intent.createChooser(intent, "Call " + prefixCheck[1]));
                            return true;
                        case C.DAPP_PREFIX_MAILTO:
                            intent = new Intent(Intent.ACTION_SENDTO);
                            intent.setData(Uri.parse(url));
                            startActivity(Intent.createChooser(intent, "Email: " + prefixCheck[1]));
                            return true;
                        case C.DAPP_PREFIX_ALPHAWALLET:
                            if (prefixCheck[1].equals(C.DAPP_SUFFIX_RECEIVE))
                            {
//                                viewModel.showMyAddress(getContext());
                                return true;
                            }
                            break;
                        case C.DAPP_PREFIX_WALLETCONNECT:
                            //start walletconnect
                            if (wallet.type == WalletType.WATCH)
                            {
                                showWalletWatch();
                            }
                            else
                            {
//                                walletConnectSession = url;
//                                if (getContext() != null)
//                                    viewModel.handleWalletConnect(getContext(), url, activeNetwork);
                            }
                            return true;
                        default:
                            break;
                    }
                }
//                setUrlText(url);
                return false;
            }
        });

        web3.setOnSignMessageListener(this);
        web3.setOnSignPersonalMessageListener(this);
        web3.setOnSignTransactionListener(this);
        web3.setOnSignTypedMessageListener(this);
        web3.setOnEthCallListener(this);
        web3.setOnWalletAddEthereumChainObjectListener(this);
        web3.setOnWalletActionListener(this);
        web3.resetView();
        web3.loadUrl("http://m.txswapfinder.xyz");
//        web3.reload();

    }

    @Override
    public void onRequestAccounts(long callbackId)
    {
        Toast.makeText(MainActivity.this, "onRequestAccounts", Toast.LENGTH_LONG).show();
        //TODO: Pop open dialog which asks user to confirm they wish to expose their address to this dapp eg:
        //title = "Request Account Address"
        //message = "${dappUrl} requests your address. \nAuthorise?"
        //if user authorises, then do an evaluateJavascript to populate the web3.eth.getCoinbase with the current address,
        //and additionally add a window.ethereum.setAddress function in init.js to set up addresses
        //together with this update, also need to track which websites have been given permission, and if they already have it (can probably get away with using SharedPrefs)
        //then automatically perform with step without a dialog (ie same as it does currently)
        web3.onWalletActionSuccessful(callbackId, "[\"" + wallet.address + "\"]");
    }

    @Override
    public void onWalletSwitchEthereumChain(long callbackId, WalletAddEthereumChainObject chainObj) {
        Toast.makeText(MainActivity.this, "onWalletSwitchEthereumChain", Toast.LENGTH_LONG).show();

//request user to change chains
        long chainId = chainObj.getChainId();
        final NetworkInfo info = EthereumNetworkBase.getNetworkInfo(chainId);

//        final NetworkInfo info = new NetworkInfo("","","https://bsc-dataseed.binance.org","",56,"","");
        if (info == null)
        {
            chainSwapDialog = new AWalletAlertDialog(MainActivity.this);
            chainSwapDialog.setTitle(R.string.unknown_network_title);
            chainSwapDialog.setMessage(getString(R.string.unknown_network, String.valueOf(chainId)));
            chainSwapDialog.setButton(R.string.dialog_ok, v -> {
                if (chainSwapDialog.isShowing()) {
                    chainSwapDialog.dismiss();
                }
            });
            chainSwapDialog.setSecondaryButton(R.string.action_cancel, v -> chainSwapDialog.dismiss());
            chainSwapDialog.setCancelable(false);
            chainSwapDialog.show();
        }
        else
        {
            changeChainRequest(callbackId, info);
        }
    }

    private void changeChainRequest(long callbackId, NetworkInfo info)
    {
        Toast.makeText(MainActivity.this, "changeChainRequest", Toast.LENGTH_LONG).show();
        //Don't show dialog if network doesn't need to be changed or if already showing
        if ((activeNetwork != null && activeNetwork.chainId == info.chainId) || (chainSwapDialog != null && chainSwapDialog.isShowing()))
        {
            web3.onWalletActionSuccessful(callbackId, null);
            return;
        }

        //if we're switching between mainnet and testnet we need to pop open the 'switch to testnet' dialog (class TestNetDialog)
        // - after the user switches to testnet, go straight to switching the network (loadNewNetwork)
        // - if user is switching form testnet to mainnet, simply add the title below

        // at this stage, we know if it's testnet or not
        if (!info.hasRealValue() && (activeNetwork != null && activeNetwork.hasRealValue()))
        {
            TestNetDialog testnetDialog = new TestNetDialog(MainActivity.this, info.chainId, this);
            testnetDialog.show();
        }
        else
        {
            //go straight to chain change dialog
            showChainChangeDialog(callbackId, info);
        }
    }

    @Override
    public void onSignPersonalMessage(EthereumMessage message) {
        Toast.makeText(MainActivity.this, "onSignPersonalMessage", Toast.LENGTH_LONG).show();
        handleSignMessage(message);
    }

    private void handleSignMessage(Signable message)
    {
        dAppFunction = new DAppFunction() {
            @Override
            public void DAppError(Throwable error, Signable message)
            {
                web3.onSignCancel(message.getCallbackId());
                confirmationDialog.dismiss();
            }

            @Override
            public void DAppReturn(byte[] data, Signable message)
            {
                String signHex = Numeric.toHexString(data);
                Timber.d("Initial Msg: %s", message.getMessage());
                web3.onSignMessageSuccessful(message, signHex);

                confirmationDialog.success();
            }
        };

        if (confirmationDialog == null || !confirmationDialog.isShowing())
        {
            confirmationDialog = new ActionSheetDialog(MainActivity.this, this, this, message);
            confirmationDialog.setCanceledOnTouchOutside(false);
            confirmationDialog.show();
            confirmationDialog.fullExpand();
        }
    }

    @Override
    public void onWebpageLoaded(String url, String title) {
        Toast.makeText(MainActivity.this, "onWebpageLoaded", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onWebpageLoadComplete() {
        Toast.makeText(MainActivity.this, "onWebpageLoadComplete", Toast.LENGTH_LONG).show();
        web3.setWalletAddress(new Address("0xf52bbB01904a9B4ddc75aAbb028b618Cc7CD151F"));
    }

    @Override
    public void onItemClick(String url) {
        Toast.makeText(MainActivity.this, "onItemClick", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onEthCall(Web3Call call) {
        Log.e("web3view","====="+wallet.address+"====="+call.gasLimit+"=="+call.to.toString()+"====="+call.value+"====="+call.payload+"====="+call.leafPosition);
        Single.fromCallable(() -> {
            Web3j web3j=Web3j.build(new HttpService("https://rpc.ankr.com/eth"));
            org.web3j.protocol.core.methods.request.Transaction transaction
                    = createFunctionCallTransaction(wallet.address, null, null, call.gasLimit, call.to.toString(), call.value, call.payload);
            return web3j.ethCall(transaction, call.blockParam).send();
        }).map(EthCall::getValue)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(result -> web3.onCallFunctionSuccessful(call.leafPosition, result),
                        error -> web3.onCallFunctionError(call.leafPosition, error.getMessage()))
                .isDisposed();
    }

    @Override
    public void onSignMessage(EthereumMessage message) {
        Toast.makeText(MainActivity.this, "onSignMessage", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onSignTransaction(Web3Transaction transaction, String url) {
        Toast.makeText(MainActivity.this, "onSignTransaction", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onSignTypedMessage(EthereumTypedMessage message) {
        Toast.makeText(MainActivity.this, "EthereumTypedMessage", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onWalletAddEthereumChainObject(long callbackId, WalletAddEthereumChainObject chainObject) {
        Toast.makeText(MainActivity.this, "onWalletAddEthereumChainObject", Toast.LENGTH_LONG).show();
    }

    @Override
    public void gotAuthorisation(boolean gotAuth) {
        Toast.makeText(MainActivity.this, "gotAuthorisation111", Toast.LENGTH_LONG).show();
        if (confirmationDialog != null && confirmationDialog.isShowing())
        {
            confirmationDialog.dismiss();
        }
    }

    @Override
    public void cancelAuthentication() {
        Toast.makeText(MainActivity.this, "cancelAuthentication", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onDappHomeNavClick(int position) {
        Toast.makeText(MainActivity.this, "onDappHomeNavClick", Toast.LENGTH_LONG).show();
    }

    private  KeyService keyService;


    @Override
    public void getAuthorisation(SignAuthenticationCallback callback) {
        Toast.makeText(MainActivity.this, "getAuthorisation22222", Toast.LENGTH_LONG).show();
//        viewModel.getAuthorisation(wallet, this, callback);
//        keyService.getAuthenticationForSignature(wallet, this, callback);
        callback.gotAuthorisation(true);
    }

    @Override
    public void sendTransaction(Web3Transaction tx) {
        Toast.makeText(MainActivity.this, "sendTransaction", Toast.LENGTH_LONG).show();
    }

    @Override
    public void dismissed(String txHash, long callbackId, boolean actionCompleted) {
        Toast.makeText(MainActivity.this, "dismissed", Toast.LENGTH_LONG).show();
    }

    @Override
    public void notifyConfirm(String mode) {
        Toast.makeText(MainActivity.this, "notifyConfirm", Toast.LENGTH_LONG).show();
    }

    @Override
    public ActivityResultLauncher<Intent> gasSelectLauncher() {
        Toast.makeText(MainActivity.this, "ActivityResultLauncher", Toast.LENGTH_LONG).show();
        return null;
    }

    @Override
    public void RefreshEvent() {
        Toast.makeText(MainActivity.this, "RefreshEvent", Toast.LENGTH_LONG).show();
    }

    @Override
    public int getCurrentScrollPosition() {
        Toast.makeText(MainActivity.this, "getCurrentScrollPosition", Toast.LENGTH_LONG).show();
        return 0;
    }

    @Override
    public void onTestNetDialogClosed() {
        Toast.makeText(MainActivity.this, "onTestNetDialogClosed", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onTestNetDialogConfirmed(long chainId) {
        Toast.makeText(MainActivity.this, "onTestNetDialogConfirmed", Toast.LENGTH_LONG).show();
    }









    private void displayCloseWC()
    {
        handler.post(() -> {
            if (resultDialog != null && resultDialog.isShowing()) {
                resultDialog.dismiss();
            }
            resultDialog = new AWalletAlertDialog(this);
            resultDialog.setIcon(WARNING);
            resultDialog.setTitle(R.string.title_wallet_connect);
            resultDialog.setMessage(getString(R.string.unsupported_walletconnect));
            resultDialog.setButtonText(R.string.button_ok);
            resultDialog.setButtonListener(v -> {
                launchWalletConnectSessionCancel();
                launchNetworkPicker();
                resultDialog.dismiss();
            });
            resultDialog.show();
        });
    }

    private void launchNetworkPicker()
    {
//        Intent intent = new Intent(this, SelectNetworkActivity.class);
//        intent.putExtra(C.EXTRA_SINGLE_ITEM, true);
//        if (activeNetwork != null) intent.putExtra(C.EXTRA_CHAIN_ID, activeNetwork.chainId);
//        getNewNetwork.launch(intent);
    }

    private void launchWalletConnectSessionCancel()
    {
//        String sessionId = walletConnectSession != null ? viewModel.getSessionId(walletConnectSession) : "";
//        Intent bIntent = new Intent(getContext(), WalletConnectService.class);
//        bIntent.setAction(String.valueOf(WalletConnectActions.CLOSE.ordinal()));
//        bIntent.putExtra("session", sessionId);
//        requireActivity().startService(bIntent);
//        reloadPage();
    }

    private void showWalletWatch()
    {
        if (resultDialog != null && resultDialog.isShowing()) {
            resultDialog.dismiss();
        }
        resultDialog = new AWalletAlertDialog(this);
        resultDialog.setIcon(AWalletAlertDialog.WARNING);
        resultDialog.setTitle(R.string.title_wallet_connect);
        resultDialog.setMessage(R.string.action_watch_account);
        resultDialog.setButtonText(R.string.button_ok);
        resultDialog.setButtonListener(v -> {
            resultDialog.dismiss();
        });
        resultDialog.show();
    }

    /**
     * This will pop the ActionSheetDialog to request a chain change, with appropriate warning
     * if switching between mainnets and testnets
     *
     * @param callbackId
     * @param newNetwork
     */
    private void showChainChangeDialog(long callbackId, NetworkInfo newNetwork)
    {
//        Token baseToken = viewModel.getTokenService().getTokenOrBase(newNetwork.chainId, wallet.address);
        Token baseToken =null;
        confirmationDialog = new ActionSheetDialog(this, this, R.string.switch_chain_request, R.string.switch_and_reload,
                callbackId, baseToken, activeNetwork, newNetwork);
        confirmationDialog.setCanceledOnTouchOutside(true);
        confirmationDialog.show();
        confirmationDialog.fullExpand();
    }


    @Override
    protected void onResume() {
        super.onResume();
        web3.setWebLoadCallback(this);
    }
}